package com.forte.cartdao;

import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.transaction.annotation.Transactional;

import com.forte.constant.Utility;
import com.forte.exception.InvalidParameterException;
import com.forte.productdao.Product;
import com.forte.shoppingcustomerdao.ShoppingCustomer;

@Transactional
public class CartDAOImpl implements CartDAO {

	private SessionFactory sessionFactory;

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected final Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	public void saveOrUpdate(Cart cart) {
		if (!cartExists(cart)) {
			getCurrentSession().saveOrUpdate(cart);
		}
	}

	public void saveOrUpdate(ShoppingCustomer customer) {
		getCurrentSession().saveOrUpdate(customer);
	}

	public void saveOrUpdate(CartProduct cartProduct) {
		getCurrentSession().saveOrUpdate(cartProduct);
	}

	@SuppressWarnings("rawtypes")
	public Cart foundCart(List list) {
		Cart cartFound = null;
		if (list.size() > 0) {
			cartFound = (Cart) list.get(0);
		}
		return cartFound;
	}

	/***
	 * Method that adds to cart
	 * 
	 * @throws InvalidParameterException
	 */
	public void addToCart(Cart cart, Product product, int quantity)
			throws InvalidParameterException {
		validateCart(cart, product, quantity);
		saveOrUpdate(cart);
		CartProduct findCartAndProduct = findCartAndProductInCartProduct(cart.getUserId(), product.getProductId());

		if (findCartAndProduct == null) {
			createNewCartProduct(cart, product, quantity);
		}
		else if(findCartAndProduct.getQuantity() + quantity > product.getQuantityInStock())
		{
			updateQuantityToQuantityInStock(product, findCartAndProduct);
		}
		else
		{
			incrementQuantityByOne(findCartAndProduct, quantity);
		}
	}

	private void incrementQuantityByOne(CartProduct findCartAndProduct, int quantity) {
		findCartAndProduct.setQuantity(findCartAndProduct.getQuantity() + quantity);
		saveOrUpdate(findCartAndProduct);
	}

	private void updateQuantityToQuantityInStock(Product product,
			CartProduct findCartAndProduct) {
		findCartAndProduct.setQuantity(product.getQuantityInStock());
		saveOrUpdate(findCartAndProduct);
	}

	private void createNewCartProduct(Cart cart, Product product, int quantity) {
		CartProduct newCartProduct = new CartProduct();
		newCartProduct.setUserId(cart.getUserId());
		newCartProduct.setQuantity(quantity);
		newCartProduct.setProductId(product.getProductId());
		saveOrUpdate(newCartProduct);
	}

	public CartProduct findCartAndProductInCartProduct(int cartId,
			int productId) {
		Query query = getCurrentSession().createQuery(
				"from cartproduct where cartId = :cartId and "
						+ "productId = :productId");
		query.setParameter("cartId", cartId);
		query.setParameter("productId", productId);

		CartProduct cartProduct = null;
		if (query.list().size() > Utility.ZERO) {
			cartProduct = (CartProduct) query.list().get(Utility.ZERO);
		}
		return cartProduct;
	}

	public Cart findCartById(int cartId) {
		Query query = getCurrentSession().createQuery(
				"from cart where cartId = :cartId");
		query.setParameter("cartId", cartId);
		Cart cart = null;
		if (query.list().size() > Utility.ZERO) {
			cart = (Cart) query.list().get(Utility.ZERO);
		}
		return cart;
	}

	public Boolean cartExists(Cart cart) {
		Boolean foundCart = false;
		if (cart != null) {
			if (cart.getCustomer() != null
					&& cart.getCustomer().getCustomerId() > Utility.ZERO) {
				Cart userCart = findCartById(cart.getCustomer().getCustomerId());
				if (userCart != null) {
					foundCart = true;
				}
			}
		}
		return foundCart;
	}
	
	public void removeProductFromCart(Cart cart, Product product) {
		CartProduct findCartProduct = findCartAndProductInCartProduct(cart.getUserId(), product.getProductId());
		getCurrentSession().delete(findCartProduct);
	}

	public void changeProductQuantityInCart(Cart cart, Product product,
			int quantity) throws InvalidParameterException {
		validateCart(cart, product, quantity);
		CartProduct findCartProduct = findCartAndProductInCartProduct(cart.getUserId(), product.getProductId());
		if(quantity <= Utility.ZERO)
		{
			this.removeProductFromCart(cart, product);
		}
		else if (quantity > product.getQuantityInStock())
        {
            this.updateQuantityToQuantityInStock(product, findCartProduct);
        }
		else
		{
			this.incrementQuantityByOne(findCartProduct, quantity);
		}
	}
	
	public double computeTotalValue(Cart cart) {
		double totalValue = 0.00;
		for (CartProduct product : cart.getCartProduct()) {
			totalValue += product.getProduct().getPrice() * product.getQuantity();
		}
		return totalValue;
	}

	private void validateCart(Cart cart, Product product, int quantity)
			throws InvalidParameterException {
		if (cart == null || (cart.getCustomer() == null)) {
			throw new InvalidParameterException(
					"Valid user is required for cart operations");
		}

		if (product == null) {
			throw new InvalidParameterException(
					"Please specify product to add to the cart");
		}

		if (quantity < Utility.ONE) {
			throw new InvalidParameterException(
					"The quantity you have entered is not valid.");
		}
	}

	public Cart findCartByCookeId(String cookieId) {
		Query query = getCurrentSession().createQuery(
				"from cart where cookieId = :cookieId");
		query.setParameter("cookieId", cookieId);
		Cart cart = null;
		if (query.list().size() > Utility.ZERO) {
			cart = (Cart) query.list().get(Utility.ZERO);
		}
		return cart;
	}

	public Cart findCartByIdOrCookieId(int cartId, String cookieId) {
		Query query = getCurrentSession().createQuery(
				"from cart where cartId = :cartId or cookieId = :cookieId");
		query.setParameter("cartId", cartId);
		query.setParameter("cookieId", cookieId);
		Cart cart = null;
		if (query.list().size() > Utility.ZERO) {
			cart = (Cart) query.list().get(Utility.ZERO);
		}
		return cart;
	}
	
	public Cart findCartByCartId(int cartId){
		Query query = getCurrentSession().createQuery(
				"from cart where cartId = :cartId");
		query.setParameter("cartId", cartId);
		Cart cart = null;
		if (query.list().size() > Utility.ZERO) {
			cart = (Cart) query.list().get(Utility.ZERO);
		}
		return cart;
	}
}
